package roborally.Gui;

import java.util.*;
import be.kuleuven.cs.som.annotate.*;

import roborally.Board;
import roborally.MoveOutOfBoardException;
import roborally.Pieces.*;

/**
 * This is a class for the RoboRally game with boards. It represents the interface between the internal program and the GUI. 
 * @version 2.0
 * @author Bart Opsomer 2de Bachelor Ingenieurswetenschappen Electrotechniek-Computerwetenschappen 
 * 		   Peter Opsomer 1ste Master Wiskundige IngenieursTechnieken
 */
@SuppressWarnings("rawtypes")
public class Facade implements IFacade {
	
	private Set<Board> boards;
	/**
	 * creates a facade with a board
	 * @param board the board to be played on
	 */
	public Facade(Board board) {
		boards = new HashSet<Board>();
		boards.add(board);
	}
	/**
	 * @effect this(new Board(100L,100L))
	 */
	public Facade() {
		this(new Board(100L,100L));
	}
	/**
	 * Create a new board
	 * @param width the width of the board
	 * @param height the height of the board
	 * @return 	the created board if the given dimensions are valid
	 * 			| if( isValidDimension(width, height)
	 * 			|  then result = new Board(width, height);
	 */
	@Override
	public Object createBoard(long width, long height) {
		try {
			Board board = new Board(width, height);
			boards.add(board);
			return board;
		}
		catch(IllegalArgumentException e){
			System.out.println("The given dimensions of the board are not valid");
			return null;
		}
	}
	/**
	 * Merge board2 into board1
	 * @param board1 the board into which board2 is merged
	 * @param board2 the board which is merged into board1
	 * @effect board1.merge(board2)
	 */
	@Override
	public void merge(Object board1, Object board2) {
		// put all object from board 2 in 1 if possible
		Board myBoard = (Board) board1;
		Board secondBoard = (Board) board2;
		myBoard.merge(secondBoard);
	}
	/**
	 * Creates a new Battery with given initial energy in Ws and weight in g and maxEnergy of 5000Ws
	 * @param initialEnergy the initial energy of the battery in Ws
	 * @param weight the weight of the battery in g
	 * @return result = new Battery(weight,new Energy(initialEnergy),new Energy(5000));
	 */
	@Override
	public Object createBattery(double initialEnergy, int weight) {
		Battery battery = new Battery(weight,new Energy(initialEnergy),new Energy(5000));
		return battery;
	}
	/**
	 * Put the given Battery on the given board at (x,y)
	 * @param board the board onto which the Battery is put
	 * @param x the x-coordinate of the battery on the board
	 * @param y the y-coordinate of the battery on the board
	 * @param battery the battery to be put on the board
	 * @effect board.addCarriablePiece(Position.getPosition(x,y,board), battery)
	 */
	@Override
	public void putBattery(Object board, long x, long y, Object battery) {
		Battery myBattery = (Battery) battery;
		Board myBoard = (Board) board;
		myBoard.addCarriablePiece(Position.getPosition(x,y,myBoard), myBattery);
	}

	/**
	 * Return the x-coordinate of the battery
	 * @param battery the battery in question
	 * @return result = battery.getPosition().getX()
	 * @throws 	IllegalStateException
	 * 			| if(battery.getPosition() == null)
	 */
	@Override @Basic
	public long getBatteryX(Object battery) throws IllegalStateException {
		Battery bat = (Battery) battery;
		if(bat.getPosition() == null)
			throw new IllegalStateException();
		return bat.getPosition().getX();
	}
	
	/**
	 * Return the y-coordinate of the battery
	 * @param battery the battery in question
	 * @return result = battery.getPosition().getY()
	 * @throws 	IllegalStateException
	 * 			| if(battery.getPosition() == null)
	 */
	@Override @Basic
	public long getBatteryY(Object battery) throws IllegalStateException {
		Battery bat = (Battery) battery;
		if(bat.getPosition() == null)
			throw new IllegalStateException();
		return bat.getPosition().getY();
	}
	/**
	 * Creates a new Robot with the given initialEnergy
	 * @param initialEnergy the initial energy of the robot in Ws
	 * @return result = new Robot(new Energy(initialEnergy))
	 */
	@Override 
	public Object createRobot(double initialEnergy) {
		Robot robot = new Robot(new Energy(initialEnergy));
		return robot;
	}
	/**
	 * Put the robot on board at (x,y) with orientation Orientation(orientation)
	 * @param board the board onto which to robot is to be placed
	 * @param x the x-coordinate of the battery on the board 
	 * @param y the y-coordinate of the battery on the board
	 * @param robot the robot in question
	 * @param orientation the orientation of the robot
	 * @post	| (new board ).contains(robot)
	 */
	@Override
	public void putRobot(Object board, long x, long y, Object robot,int orientation) {
		Board myBoard = (Board) board;
		Robot myRobot = (Robot) robot;
		PositionWithOrientation pos = PositionWithOrientation.getPWO(x,y,myBoard,Orientation.getOrientationFromInt(orientation));
		myRobot.setPositionWithOrientation(pos);
		myBoard.addRobot(myRobot);
	}
	/**
	 * Return the x-coordinate of the robot
	 * @param battery the robot in question
	 * @return  |result = robot.getPosition().getX()
	 * @throws 	IllegalStateException
	 * 			| if(battery.getPosition() == null)
	 */
	@Override @Basic
	public long getRobotX(Object robot) throws IllegalStateException {
		Robot myRobot = (Robot) robot;
		if(myRobot.getPosition() == null)
			throw new IllegalStateException();
		return myRobot.getPosition().getX();
	}
	/**
	 * Return the y-coordinate of the robot
	 * @param battery the robot in question
	 * @return 	|result = robot.getPosition().getY()
	 * @throws 	IllegalStateException
	 * 			| if(battery.getPosition == null)
	 */
	@Override @Basic
	public long getRobotY(Object robot) throws IllegalStateException {
		Robot myRobot = (Robot) robot;
		if(myRobot.getPosition() == null)
			throw new IllegalStateException();
		return myRobot.getPosition().getY();
	}
	/**
	 * Returns the orientation of the robot as an integer
	 * @param robot
	 * @return result = robot.getPositionWithOrientation().getOrientation().getInt()
	 */
	@Override @Basic
	public int getOrientation(Object robot) {
		Robot myRobot = (Robot) robot;
		return myRobot.getPositionWithOrientation().getOrientation().getInt();
	}
	/**
	 * Returns the current energy of the robot
	 * @param robot the robot of which the energy is requested
	 * @return robot.getEnergy().getAmount()
	 */
	@Override @Basic
	public double getEnergy(Object robot) {
		Robot myRobot = (Robot) robot;
		return myRobot.getEnergy().getAmount();
	}
	/**
	 * Move the robot 1 step forward
	 * @param robot the robot to be moved
	 * @effect robot.getPosition().getBoard().moveRobot(robot)
	 */
	@Override
	public void move(Object robot) {
		Robot myRobot = (Robot) robot;
		Board myBoard = myRobot.getPosition().getBoard();
		myBoard.moveRobot(myRobot);
	}
	/**
	 * Turn the robot clockWise
	 * @param robot the robot to be turned
	 * @effect robot.turnClockwise();
	 */
	@Override
	public void turn(Object robot) {
		Robot myRobot = (Robot) robot;
		myRobot.turnClockwise();
	}
	/**
	 * Return the inventory of the given robot
	 * @param robot the robot of which inventory is requested
	 * @return | result = robot.getBatteries()
	 */
	@Override @Basic
	public Set<Battery> getPossessions(Object robot) {
		Robot myRobot = (Robot) robot;
		return myRobot.getBatteries();
	}
	/**
	 * Pick up the given item if possible
	 * @param robot the robot to pick up the item
	 * @param item the item to be picked up
	 * @post  	|if(!robot.isTerminated() && !item.isTerminated() )
	 * 			|	then new robot.getInventory().contains(item)
	 */
	@Override
	public void pickUp(Object robot, Object item) {
		Robot myRobot = (Robot) robot;
		CarriablePiece myItem = (CarriablePiece) item;
		if(myRobot == null || myItem == null)
			return;
		if(myRobot.isTerminated() || myItem.isTerminated())
			return;
		if(myRobot.getPosition().equals(myItem.getPosition())) {
			Board myBoard = myItem.getPosition().getBoard();
			myBoard.removeItem(myItem);
			myRobot.addItem(myItem);
			myItem.setPosition(null);
		}
		else {
			System.out.println("Robot is at "+myRobot.getPosition().toString() + 
					"and the object you want to pickup is at"+myItem.getPosition().toString());
		}
		
	}
	/**
	 * Use the item if the robot has it in its inventory
	 * @effect  robot.use(item)
	 */
	@Override
	public void use(Object robot, Object item) {
		Robot myRobot = (Robot) robot;
		CarriablePiece myItem = (CarriablePiece) item;
		if(myRobot.getInventory().contains(myItem)) {
			myRobot.use(myItem);
		}
	}
	/**
	 * Let the robot drop the given item if the robot has it in its inventory
	 * @effect	robot.drop(item)
	 */
	@Override
	public void drop(Object robot, Object item) {
		Robot myRobot = (Robot) robot;
		CarriablePiece myItem = (CarriablePiece) item;
		myRobot.drop(myItem);
	}
	/**
	 * @return | result = 1
	 */
	@Override @Immutable
	public int isMinimalCostToReach17Plus() {
		return 1;
	}
	
	/**
	 * Returns the minimal energy for the robot to reach (x,y) on the board of the robot
	 * @return 	the minimal energy for the robot to reach (x,y) on the board of the robot
	 * 			| result = myRobot.getEnergyRequiredToReach(Position.getPosition(x,y,board) )
	 */
	@Override
	public double getMinimalCostToReach(Object robot, long x, long y) {
		Robot myRobot = (Robot) robot;
		Board board = myRobot.getPosition().getBoard();
		try{
			return myRobot.getEnergyRequiredToReach(Position.getPosition(x,y,board) );
		}
		catch(MoveOutOfBoardException exc) {
			System.out.println("The given point (x,y) is not a valid location");
			return 0.0;
		}
		catch(IllegalArgumentException exc) {
			System.out.println("The robot is terminated, and cannot move");
			return 0.0;
		}
		catch(Exception e) {
			// not possible
			System.err.println("Exception: " + e);
			return 0.0;
		}
	}
	/**
	 * @return 	| result = 1
	 */
	@Override @Immutable
	public int isMoveNextTo18Plus() {
		return 1;
	}
	/**
	 * Move the 2 robots as close as possible with the minimal energy
	 * @effect  robot.moveNextTo(other)
	 */
	@Override
	public void moveNextTo(Object robot, Object other) {
		try {
			( (Robot) robot).moveNextTo((Robot) other);
		}
		catch(Exception e) {
			System.err.println("Exception: " + e);
		}
	}
	/**
	 * Let the robot shoot in the direction of the robot
	 * @effect 	robot.getPosition().getBoard().shoot(robot)
	 */
	@Override
	public void shoot(Object robot){
		Robot myRobot = (Robot) robot;
		Board board = myRobot.getPosition().getBoard();
		board.shoot(myRobot);
		
	}
	/**
	 * Create a new wall
	 * @return 	returns a new wall
	 * 			| result = new Wall()
	 */
	@Override
	public Object createWall() {
		Wall wall = new Wall();
		return wall;
	}
	/**
	 * Put the wall on this board at (x,y)
	 * @effect	board.addWall(wall.setPosition(Position.getPosition(x,y,myBoard)))
	 */
	@Override
	public void putWall(Object board, long x, long y, Object wall) {
		Board myBoard = (Board) board;
		Wall myWall = (Wall) wall;
		myWall.setPosition(Position.getPosition(x,y,myBoard));
		myBoard.addWall(myWall);
		
	}
	/**
	 * Returns the x-coordinate of the given wall
	 * @return 	the x-coordinate of the given wall
	 * 			| result = wall.getPosition().getX()
	 * @throws	IllegalStateException
	 * 			| if(wall.getPosition() ==null)
	 */
	@Override @Basic
	public long getWallX(Object wall) throws IllegalStateException {
		Wall theWall = (Wall) wall;
		Position pos = theWall.getPosition();
		if(pos == null)
			throw new IllegalStateException();
		return pos.getX();
	}
	/**
	 * returns the y-coordinate of the given wall
	 * @return 	the y-coordinate of the given wall
	 * 			| result = wall.getPosition().getX()
	 * @throws	IllegalStateException
	 * 			| if(theWall.getPosition() ==null)
	 */
	@Override @Basic
	public long getWallY(Object wall) throws IllegalStateException {
		Wall theWall = (Wall) wall;
		Position pos = theWall.getPosition();
		if(pos == null)
			throw new IllegalStateException();
		return pos.getY();
	}
	/**
	 * Get all robots on the given board
	 * @return	all robots on the board
	 * 			| result = board.getRobots()
	 */
	@Override
	public Set<Robot> getRobots(Object board) {
		Board myBoard = (Board) board;
		Set<Robot> robots = myBoard.getRobots();
		return new HashSet<Robot>(robots);
	}
	/**
	 * Get all batteries on the given board
	 * @return	all robots on the board
	 * 			| result = board.getBatteries()
	 */
	@Override
	public Set<Battery> getBatteries(Object board) {
		Board myBoard = (Board) board;
		Set<Battery> batteries = myBoard.getBatteries();
		return new HashSet<Battery>(batteries);
	}
	/**
	 * Get all walls on the given board
	 * @return	all walls on the board
	 * 			| result = board.getWalls()
	 */
	@Override
	public Set<Wall> getWalls(Object board) {
		Board myBoard = (Board) board;
		Set<Wall> walls = myBoard.getWalls();
		return new HashSet<Wall>(walls);
	}

}
